use rust_decimal::Decimal;
use serde::Serialize;

use crate::{error::{ec, new_err_msg, AppErr, SqlxErr}, store::get_pool};

#[derive(Debug, Serialize)]
pub struct TbProduct {
    pub id: u64,
    name: String,
    heat_time: u32,
    #[serde(with = "serde_bytes")]
    image: Box<[u8]>,
    expiration: u32,
    price: Decimal,
}

pub async fn create(
    name: &str,
    heat_time: u32,
    image: &[u8],
    expiration: u32,
    price: Decimal,
) -> Result<u64, AppErr> {
    let ret = sqlx::query!(
        r#"
        INSERT INTO tb_product (name, heat_time, image, expiration, price) 
        VALUES (?, ?, ?, ?, ?)
    "#,
        name,
        heat_time,
        image,
        expiration,
        price
    )
    .execute(get_pool())
    .await;
    match ret {
        Err(SqlxErr::Database(err)) if err.is_unique_violation() => {
            Err(new_err_msg(ec::PRODUCT_IS_EXISTS, ec::PRODUCT_IS_EXISTS_MSG))
        }
        Err(e) => Err(e.into()),
        Ok(v) => Ok(v.last_insert_id())
    }
}

pub async fn update(
    id: u64,
    name: &str,
    heat_time: u32,
    image: &[u8],
    expiration: u32,
    price: Decimal,
) -> Result<(), AppErr> {
    let ret = sqlx::query!(
        r#"
        UPDATE tb_product 
        SET name = ?, 
        heat_time = ?, 
        image = ?, 
        expiration = ?, 
        price = ?
        WHERE id = ?
    "#,
        name,
        heat_time,
        image,
        expiration,
        price,
        id
    )
    .execute(get_pool())
    .await;
    match ret {
        Ok(_) => Ok(()),
        Err(SqlxErr::Database(err)) if err.is_unique_violation() => {
            Err(new_err_msg(ec::PRODUCT_IS_EXISTS, ec::PRODUCT_IS_EXISTS_MSG))
        }
        Err(e) => Err(e.into())
    }
}

pub async fn query_all() -> Result<Box<[TbProduct]>, SqlxErr> {
    let ps = sqlx::query_as!(
        TbProduct,
        "SELECT id, name, heat_time, image, expiration, price FROM tb_product"
    )
    .fetch_all(get_pool())
    .await?;

    Ok(ps.into_boxed_slice())
}

pub async fn delete(id: u64) -> Result<(), SqlxErr> {
    sqlx::query!("DELETE FROM tb_product WHERE id = ?", id)
        .execute(get_pool())
        .await?;
    Ok(())
}
